
import java.util.*;
import java.io.*;
import java.lang.reflect.*;

/**
   This program demonstrates a custom class loader that decrypts
   class files.
 */
public class UnScrambleAndLoad
{
	private static String filename = "StartUp";
	private static String password = "";
	private static int key = 0;

	public static void main(String[] args)
	{
		JavaHowTo.Instance.cls();
		InputStreamReader converter = new InputStreamReader(System.in);
		BufferedReader inbuf = new BufferedReader(converter);
		String CurLine;
		boolean done = false;
		
		while(!done)
		{
			System.out.println("Provide the key for starting SCADA (type 'quit' to exit):");
			
		try {
			CurLine = inbuf.readLine();

			if (!(CurLine.equals("quit"))){
				int newkey = 0;
				password = CurLine;
				for(char b: password.toCharArray())
				{
					newkey += b; 
				}
				runClass(filename, newkey);
				
			}

			done = true;	
			
		} catch (IOException e) {
			System.out.println("Error reading input - try again!");
		}
		catch(WrongKeyException wke)
		{
			JavaHowTo.Instance.keepColors();
			JavaHowTo.Instance.setColor(JavaHowTo.Instance.BACKGROUND_RED, JavaHowTo.Instance.FOREGROUND_WHITE);

			System.out.println(wke.toString());

			JavaHowTo.Instance.restoreColors();
		}
		}
		
		
	}
	/**
      Runs the main method of a given class.
      @param name the class name
      @param key the decryption key for the class files
	 */
	public static void runClass(String name, int key) 
	throws WrongKeyException
	{
		try
		{
			ClassLoader loader = new CryptoClassLoader(key);
			Class c = loader.loadClass(name);
			String[] args = new String[] {};

			Method m = c.getMethod("main", new Class[] { args.getClass() });
			m.invoke(null, new Object[] { args });
		}
		catch (Throwable e)
		{
			WrongKeyException wke = new WrongKeyException("Wrong key provided", password);
			wke.setStackTrace(e.getStackTrace());
			
			throw wke;
		
		}
	}
}

/**
   This class loader loads encrypted class files.
 */
class CryptoClassLoader extends ClassLoader
{
	/**
      Constructs a crypto class loader.
      @param k the decryption key
	 */
	public CryptoClassLoader(int k)
	{
		key = k;
	}

	@Override
	protected Class findClass(String name)
	throws ClassNotFoundException
	{
		byte[] classBytes = null;
		try
		{
			classBytes = loadClassBytes(name);
		}
		catch (IOException exception)
		{
			throw new ClassNotFoundException(name);
		}

		Class cl = defineClass(name, classBytes,
				0, classBytes.length);
		if (cl == null)
			throw new ClassNotFoundException(name);
		return cl;
	}

	/**
      Loads and decrypt the class file bytes.
      @param name the class name
      @return an array with the class file bytes
	 */
	private byte[] loadClassBytes(String name)
	throws IOException
	{
		String cname = name.replace('.', '/') + ".encrypted";

		FileInputStream in = null;
		try
		{
			in = new FileInputStream(cname);
			ByteArrayOutputStream buffer
			= new ByteArrayOutputStream();
			int ch;
			while ((ch = in.read()) != -1)
			{
				byte b = (byte)(ch - key);
				buffer.write(b);
			}
			in.close();
			return buffer.toByteArray();
		}
		finally
		{
			if (in != null)
				in.close();
		}
	}

	private Map classes = new HashMap();
	private int key;
}

class WrongKeyException extends Exception
{
	String err = "";
	String key = "";
	
	public WrongKeyException()
	{
		err = "Unspecified key error";
		key = "Key not specified";
	}
	
	public WrongKeyException(String errorMsg)
	{
		err = errorMsg;
		key = "Key not specified";
	}
	
	public WrongKeyException(String errorMsg, String key)
	{
		err = errorMsg;
		this.key = key;
	}
	
	public String getErrorMessage()
	{
		return err;
	}
	
	public String getKey()
	{
		return key;
	}
	
	@Override
	public String toString()
	{
		return err+"\nProvided Key:\t"+key;
	}
}
